// Effective C++ 条款39：明智而审慎地使用私有继承
// 条款39的核心是："明智而审慎地使用私有继承"（Use private inheritance judiciously）。

// 详细解释
// 私有继承的本质
// 私有继承与公有继承有根本不同：
// -公有继承表示"is-a"（是一种）关系
// -私有继承表示"is-implemented-in-terms-of"（根据某物实现出）关系

// 私有继承意味着：
// 1.基类的公有和保护成员在派生类中变成私有成员
// 2.派生类不能被视为基类的一种类型
// 3.基类的实现被继承，但接口不被继承

// 私有继承 vs 复合
// 条款38建议使用复合来表示"is-implemented-in-terms-of"关系，那么什么时候应该选择私有继承而不是复合呢？

// 私有继承的主要使用场景：
// 1.需要访问基类的保护成员
// 2.需要重写基类的虚函数
// 3.需要控制对象的内存布局（如空基类优化）

// 示例：使用私有继承
class Timer
{
protected:
    virtual void onTick() const; // 保护的虚函数
public:
    void start();
    void stop();
};

class Widget : private Timer
{ // 私有继承
protected:
    virtual void onTick() const override; // 重写保护的虚函数
public:
    void start() { Timer::start(); } // 选择性地暴露基类功能
};

// 在这个例子中，Widget私有继承了Timer，以便重写onTick虚函数。外部代码不能将Widget视为Timer，也不能直接访问Timer的方法。

// 示例：使用复合实现相同功能
class Widget
{
private:
    class WidgetTimer : public Timer
    {
    protected:
        virtual void onTick() const override;
    };

    WidgetTimer timer;

public:
    void start() { timer.start(); }
};

// 空基类优化（EBO）
// 当基类没有非静态数据成员时，私有继承可以利用空基类优化来减少对象大小：
class Empty
{
}; // 空类

// 使用复合
class HoldsEmpty1
{
private:
    Empty e;  // 通常占用至少1字节
    int data; // 4字节
}; // 通常大小为8字节（考虑对齐）

// 使用私有继承
class HoldsEmpty2 : private Empty
{
private:
    int data; // 4字节
}; // 通常大小为4字节（EBO）
// 在某些编译器中，HoldsEmpty2的大小会小于HoldsEmpty1，因为空基类优化允许派生类不为空基类分配存储空间。

// 实际应用
// 何时使用私有继承
//      1.需要访问保护成员：基类有保护成员或保护接口，派生类需要访问
//      2.需要重写虚函数：基类定义了虚函数接口，派生类需要特化行为
//      3.空基类优化：基类没有数据成员，使用私有继承可以节省空间
//      4.控制接口暴露：需要使用基类实现但不希望暴露其接口
// 何时避免使用私有继承
//      1.有更简单的替代方案：大多数情况下，复合是更清晰的选择
//      2.不需要访问保护成员或重写虚函数：如果只需要基类的公共接口，使用复合
//      3.接口重要性大于实现：如果重点是接口而非实现细节，使用复合更清晰
// 设计技巧
//      1.先考虑复合：默认选择复合来表示"is-implemented-in-terms-of"关系
//      2.仅在必要时使用私有继承：只有当复合无法满足需求时才考虑私有继承
//      3.使用using声明：如果需要暴露基类的某些成员，使用using声明